package com.tianji.learning.service.impl;

import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.PointsStatisticsVO;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.service.IPointsRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-11-17
 */
@Service
@RequiredArgsConstructor
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {
    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 新增积分
     *
     * @param userId 加积分的用户id
     * @param points 增加积分数
     * @param type   类型
     */
    @Override
    public void addPointsRecord(Long userId, Integer points, PointsRecordType type) {
        //1.判断类型是否有上限
        if (type.getMaxPoints() > 0) {
            //2.如果有上限判断是否已经超过上限
            //2.1查询数据库
            int max = getTodayMaxPoints(userId, type);
            if (max >= type.getMaxPoints()) {
                return;
            }
            //2.2.如果没超过上限判断增加后是否会超过上限
            if (points + max > type.getMaxPoints()) {
                points = type.getMaxPoints() - max;
            }
        }

        //3.新增积分
        PointsRecord record = new PointsRecord();
        record.setUserId(userId);
        record.setPoints(points);
        record.setType(type);
        this.save(record);

        // 4.更新总积分到Redis
        LocalDate now = LocalDate.now();
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + now.format(DateUtils.POINTS_BOARD_SUFFIX_FORMATTER);
        stringRedisTemplate.opsForZSet().incrementScore(key, userId.toString(), points);
    }

    /**
     * 查询今日积分获取情况
     *
     * @return
     */
    @Override
    public List<PointsStatisticsVO> queryMyPointsToday() {
        //1.获取用户id
        Long userId = UserContext.getUser();

        //2.获取当前日期
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime start = DateUtils.getDayStartTime(now);
        LocalDateTime end = DateUtils.getDayEndTime(now);

        //3.查询数据库
        List<PointsRecord> list = this.query().select("type,sum(points) points")
                .eq("user_id", userId)
                .between("create_time", start, end)
                .groupBy("type")
                .list();
        if (ObjectUtils.isEmpty(list)) {
            return Collections.emptyList();
        }

        //4.封装vo返回
        ArrayList<PointsStatisticsVO> vos = new ArrayList<>(list.size());
        for (PointsRecord p : list) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setPoints(p.getPoints());
            vo.setType(p.getType().getDesc());
            vo.setMaxPoints(p.getType().getMaxPoints());
            vos.add(vo);
        }


        //补充
        //5.处理没有积分的情况
        //收集查询结果中有的类型到集合里
        Set<String> typeSet = vos.stream().map(PointsStatisticsVO::getType).collect(Collectors.toSet());
        //过滤出所有不在集合中的类型并放入集合中
        List<PointsRecordType> collect = Arrays.stream(PointsRecordType.values()).filter(p -> !typeSet.contains(p.getDesc())).collect(Collectors.toList());
        for (PointsRecordType p : collect) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setPoints(0);
            vo.setType(p.getDesc());
            vo.setMaxPoints(p.getMaxPoints());
            vos.add(vo);
        }


        return vos;
    }

    private int getTodayMaxPoints(Long userId, PointsRecordType type) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime start = DateUtils.getDayStartTime(now);
        LocalDateTime end = DateUtils.getDayEndTime(now);
        //select sum(points) points from pints_record where userid = ?
        //and type = ? and create_time between ? and ?
        PointsRecord one = this.query().select("sum(points) points")
                .eq("user_id", userId)
                .eq("type", type)
                .between("create_time", start, end)
                .one();
        if (ObjectUtils.isEmpty(one)) {
            return 0;
        }
        return one.getPoints();
    }
}
